﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;
using LEARNREN.COM.UI.LabelProviders;
using LEARNREN.COM.BLL;
using LEARNREN.COM.TOOL;

namespace LEARNREN.COM.UI.Template
{
    public abstract class HtmlTemplate : IDisposable, ILabelContainer
    {
        /// <summary>
        /// 分组逻辑工厂，可获得一系列共用数据连接的逻辑对象
        /// </summary>
        protected GroupLogicFactory groupLogicFactory = null;

        /// <summary>
        /// 上下文对象
        /// </summary>
        protected TemplateContext context = null;

        /// <summary>
        /// 模板文件的物理路径
        /// </summary>
        string templateFile = null;

        /// <summary>
        /// 模板虚拟文件夹，以~开头
        /// </summary>
        protected string templateVirtualDirectory = null;

        /// <summary>
        /// 模板绝对文件夹，相对于网站根路径的绝对文件夹以/开头
        /// </summary>
        protected string templateAbsoluteDirectory = null;

        /// <summary>
        /// 构造一个模板处理实体
        /// </summary>
        /// <param name="tmpFile">模板文件的虚拟路径，以~开头</param>
        public HtmlTemplate(string tmpFile)
        {
            this.templateFile = System.Web.Hosting.HostingEnvironment.MapPath(tmpFile);
            this.templateVirtualDirectory = GetVirtualTemplateFolder(tmpFile);
            this.templateAbsoluteDirectory = System.Web.VirtualPathUtility.ToAbsolute(templateVirtualDirectory);
            this.groupLogicFactory = new GroupLogicFactory();
            this.context = new TemplateContext
            {
                Container = this,
                State = null,
                TemplateAbsoluteDirectory = templateAbsoluteDirectory,
                TemplateVirtualDirectory = templateVirtualDirectory
            };
        }

        /// <summary>
        /// 将页面内容渲染到流，可以为页面输出流
        /// </summary>
        /// <param name="stream"></param>
        public void RenderToStream(Stream stream)
        {
            string html = GetHtmlResult();
            byte[] buffer = Encoding.Default.GetBytes(html);
            stream.Write(buffer, 0, buffer.Length);
        }

        /// <summary>
        /// 将页面内容写入文件
        /// </summary>
        /// <param name="fileName">要写入的文件名，支持以~开头的绝对路径</param>
        public void RenderToFile(string fileName)
        {
            File.WriteAllText(System.Web.Hosting.HostingEnvironment.MapPath(fileName), GetHtmlResult(), Encoding.UTF8);
        }

        private string GetHtmlResult()
        {
            try
            {
                string result = string.Empty;
                string templateHtml = File.ReadAllText(this.templateFile, Encoding.UTF8);
                // 处理模板中Include类型标签
                templateHtml = ProcessIncludeLabel(templateHtml);
                // 处理模板中lr_tmpdir类型标签
                templateHtml = ProcessTmpDirLabel(templateHtml);

                // 设置上下文，辅助对象
                SetContextData();

                // 处理关闭形式的标签
                List<ILabelProvider> labels = GetClosedLabel(templateHtml);
                templateHtml = TranslateAndReplace(labels, templateHtml);

                // 处理其他所有标签
                labels = GetSingleLabels(templateHtml);
                templateHtml = TranslateAndReplace(labels, templateHtml);

                // 最后再处理一遍lr_tmpdir类型标签
                result = ProcessTmpDirLabel(templateHtml);
                return result;
            }
            catch (Exception ex)
            {
                Loger.LogErr(ex);
                return string.Empty;
            }
        }

        protected virtual void SetContextData()
        {

        }

        /// <summary>
        /// 处理并合并Include类型的标签
        /// </summary>
        /// <param name="template">模板HTML</param>
        /// <returns></returns>
        private string ProcessIncludeLabel(string template)
        {
            string pattern =LabelPattern.INCLUDEPATTERN;
            List<ILabelProvider> includeLabels = LabelContenerHelper.GetLabels(template, pattern, groupLogicFactory, context);
            if (includeLabels.Count > 0)
            {
                StringBuilder sb = new StringBuilder(template);
                while (includeLabels.Count > 0)
                {
                    foreach (var includeLabel in includeLabels)
                    {
                        string lbStr = includeLabel.GetHtml();
                        if (lbStr != includeLabel.LabelString)
                        {
                            sb.Replace(includeLabel.LabelString, lbStr);
                        }
                    }
                    includeLabels = LabelContenerHelper.GetLabels(sb.ToString(), pattern, groupLogicFactory, context);
                }
                return sb.ToString();
            }
            return template;
        }

        /// <summary>
        /// 处理模板中所有引用tmpdir标签的位置
        /// 这样会节省资源，不用生成label对象。
        /// </summary>
        /// <param name="template">模板HTML</param>
        /// <returns></returns>
        private string ProcessTmpDirLabel(string template)
        {
            //string pattern = @"\{lr_tmpdir:[\w\W]*?\}";
            //List<ILabelProvider> tmpDirLabels = GetLabels(template, pattern);
            //if (tmpDirLabels.Count > 0)
            //{ 

            //}

            return template.Replace("{lr_tmpdir:/}", this.templateAbsoluteDirectory);
        }

        /// <summary>
        /// 处理所有标签，并替换模板中的内容，最后得到的是页面输出结果
        /// </summary>
        /// <param name="labels">标签内容</param>
        /// <param name="template">模板</param>
        /// <returns></returns>
        protected virtual string TranslateAndReplace(List<ILabelProvider> labels, string template)
        {
            StringBuilder sbTemplate = new StringBuilder(template);
            foreach (var item in labels)
            {
                try
                {
                    string lbStr = item.GetHtml();
                    if (lbStr != item.LabelString)
                    {
                        sbTemplate.Replace(item.LabelString, lbStr);
                    }
                }
                catch (Exception ex)
                {
                    Loger.LogErr(ex);
                    continue;
                }
            }
            return sbTemplate.ToString();
        }

        /// <summary>
        /// 获取完整关闭的标签。
        /// 形如
        /// {lr_pdl:cur,cur,20,20}
        ///     &lt;header&gt;{lr_atp:/}&lt;/header&gt;
        ///     &lt;item&gt;&lt;/item&gt;
        ///     &lt;footer&gt;&lt;/footer&gt;
        /// {/lr_pdl}
        /// </summary>
        /// <param name="html">模板的html内容</param>
        /// <returns></returns>
        private List<ILabelProvider> GetClosedLabel(string html)
        {
            string pattern = LabelPattern.CLOSEDPATTERN;
            return LabelContenerHelper.GetLabels(html, pattern, groupLogicFactory, context);
        }

        /// <summary>
        /// 获取模板中所有的标签
        /// </summary>
        /// <param name="html">模板的html内容</param>
        /// <returns></returns>
        private List<ILabelProvider> GetSingleLabels(string html)
        {
            return LabelContenerHelper.GetLabels(html, groupLogicFactory, context);
        }

        /// <summary>
        /// 根据模板文件虚拟路径获取模板文件夹根路径
        /// </summary>
        /// <param name="templateFilePath">模板文件虚拟路径</param>
        /// <returns></returns>
        private string GetVirtualTemplateFolder(string templateFilePath)
        {
            return System.Web.VirtualPathUtility.GetDirectory(templateFilePath);
        }

        public void Dispose()
        {
            groupLogicFactory.Dispose();
        }
    }
}
